import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;

public class NeuronInputField extends DrawObject
{
	// DrawObject abilities:
	@Override
	public boolean IsNeuronInputField()
	{
		return true;
	}

	@Override
	public boolean CanBeDeletedByUser()
	{
		return false;
	};

	@Override
	public boolean CanBeExcitedByUser()
	{
		return true;
	};

	@Override
	public boolean CanBeMovedByUser()
	{
		return false;
	}

	@Override
	public boolean CanBeSelectedByUser()
	{
		return false;
	}
	// end of DrawObject abilities

	public NeuronInputField(Point Pos)
	{
		SetPosNoAlignOnGrid(Pos);
		SetSize(new Point(80, 20)); // we need 10 x-pixels for excitement bar plus 10 pixels gap (looks better)
		InitializeExcitement(ExcitementOnceMilliseconds);
	};

	@Override
	protected void AddExcitementForNextTick(double ExcitementAdd)
	{
		// Neuron: NeuronInputNodes DIRECTLY call this.ExciteAsPartner() instead!

		// reset
		ExcitementCurrent = 0.0;
		ExcitementAddForNextTick = 0.0;
	};

	@Override
	protected void AfterCollectingAllExcitementForNextTick()
	{
		// reset
		ExcitementCurrent = 0.0;
		ExcitementAddForNextTick = 0.0;
	};

	@Override
	public void Draw(Graphics g)
	{
		if (!(IsWithinWindowBounds(this, 0)))
			return;

		// background (overdraw grid)
		if (IsHighlighted || IsPartOfMultiSelection)
			SetColor(g, Color.GREEN);
		else
			SetColor(g, Color.LIGHT_GRAY);
		FillRect(g, Pos.x, Pos.y, Size.x, Size.y);

		// strength bar
		if (IsNegative)
			SetColor(g, Color.BLUE);
		else
			SetColor(g, Color.MAGENTA);
		FillRect(g, Pos.x, Pos.y, Strength * Size.x / StrengthMax, Size.y);

		// ExcitementTriggerValue marking
		/*
		SetColor(g, Color.RED);
		g.drawRect(Pos.x + (int) (ExcitementTriggerValue * Size.x), Pos.y, 0, Size.y);
		*/

		// frame
		if (IsHighlighted || IsPartOfMultiSelection)
			SetColor(g, Color.BLACK);
		else
			SetColor(g, GetExcitementColor());
		DrawRect(g, Pos.x, Pos.y, Size.x, Size.y);

		// level markings
		/*
		SetColor(g, Color.BLACK);
		int Stepx = Size.x / StrengthMax;
		for (int m = 0; m < StrengthMax; m ++)
		{
			g.drawLine(Pos.x + m * Stepx, Pos.y + 1, Pos.x + m * Stepx, Pos.y + 1 + 4);
		}
		*/
	}

	protected void ExciteAsPartner(double ExcitementCurrent, DrawObject Sender)
	{
		// NOTE: we need to forward excitement instantly to avoid
		// annoying delays, but Neuron must first "collect" all
		// excitement values (negative ones might come along, too)
		// before "deciding" if to activate NeuronOutputNode!

		double Sign = +1.0;

		if (IsNegative && !(((Neuron) this.ExcitementPartner).IsInLearningNeuronState))
			Sign = -1.0;

		double FieldWeight = Sign * (double) Strength / (double) StrengthMax;

		double ForwardedWeightedExcitement = LimitExcitementAllowNegative(ExcitementCurrent * FieldWeight);

		if (DoesActivate(ExcitementCurrent)) // do NOT use WEIGHTED Excitement value, because it is only important if InputNODE was/is activated!
		{
			ExcitementPartnerServedInThisTick = true;
		}

		((Neuron) this.ExcitementPartner).ExciteAsPartner(ForwardedWeightedExcitement, this);

		ShiftExcitementHistory(ExcitementCurrent);
	}

	@Override
	protected void LoseExcitementAndAddExcitementForNextTick()
	{
		// uses the ExciteAsPartner() excitement donation scheme!

		ExcitementCurrent = 0.0;
		ExcitementAddForNextTick = 0.0;
		ExcitementPartnerServedInThisTick = false;
	}

	@Override
	public void Move(Point MoveAmount)
	{
		SetPosNoAlignOnGrid(new Point(Pos.x + MoveAmount.x, Pos.y + MoveAmount.y));
	}

	@Override
	public void ResetState()
	{ // meant to be called for EVERY DrawObject before beginning a new simulation

		if (((Neuron) this.ExcitementPartner).IsInLearningNeuronState && IsNegative)
			Strength = StrengthMax;
		else if (((Neuron) this.ExcitementPartner).IsInLearningNeuronState)
			Strength = 0;

		ExcitementCurrent = 0.0;
		ExcitementAddForNextTick = 0.0;

		ExcitementPartnerServedInThisTick = false;

		for (int m = 0; m < ExcitementHistoryLength; m++)
			ExcitementHistory[m] = 0.0;
		ExcitementHistoryWriteIndex = 0;
	}

	public void Toggle()
	{
		if (IsNegative && (((Neuron) this.ExcitementPartner).IsInLearningNeuronState))
		{
			Strength = StrengthMax;
		}
		else if (!(((Neuron) this.ExcitementPartner).IsInLearningNeuronState))
		{
			Strength += StrengthIncreaseStep;
			if (Strength > StrengthMax)
				Strength = 0;
		}
	}

	@Override
	public void ToggleIsInLearningNeuronState()
	{
		ParentDrawObject.ToggleIsInLearningNeuronState();
	}

	public void ToggleIsNegative()
	{
		IsNegative = !IsNegative;

		if ((((Neuron) this.ExcitementPartner).IsInLearningNeuronState) && IsNegative)
		{
			SetStrength(StrengthMax); // means internal weight 1.0, to avoid confusion of user
			SetStrengthTrigger(StrengthMax); // not used anyway
		}
		if ((((Neuron) this.ExcitementPartner).IsInLearningNeuronState) && !IsNegative)
		{
			SetStrength(0); // "nothing learned yet"
			SetStrengthTrigger(StrengthMax); // not used anyway
		}
	}
}
